﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using JOATServer.SocietyFramework.Core.XML;

namespace JOATServer.SocietyFramework.Core
{
    /// <summary>
    /// Abstract class which manages a society of automatons. It performs adding/removing/starting/stopping
    /// automatons and persistence operations. It is also responsible with sending and receiving automatons
    /// from the remote application.
    /// </summary>
    public abstract class SocietyManager
    {
        protected Dictionary<string, BaseAutomaton> automatons;

        protected AutomataTransferAutomaton automataTransferAutomaton;

        public SocietyManager()
        {
            this.automatons = new Dictionary<string, BaseAutomaton>();
        }

        public void StartAutomataTransfer()
        {
            this.automataTransferAutomaton = new AutomataTransferAutomaton(this);
            MessageRouter.Instance.Subscribe("AutomataTransferAutomaton", this.automataTransferAutomaton);
            this.automataTransferAutomaton.Start();
            MessageRouter.Instance.SendLocal(SocietyInformation.UI_MANAGER_NAME,
                new Message("SocietyManager", "Automata transfer started."));
        }

        public void StopAutomataTransfer()
        {
            this.automataTransferAutomaton.StopSafely(() =>
            {
                MessageRouter.Instance.SendLocal(SocietyInformation.UI_MANAGER_NAME,
                    new Message("SocietyManager", "Automata transfer successfully stopped."));
            });
        }

        /// <summary>
        /// Returns the automaton that identifies with the name given as parameter.
        /// </summary>
        /// <param name="name">the name which this automaton identifies with</param>
        /// <returns>reference to the automaton or null if there is no automaton with that name</returns>
        public BaseAutomaton GetAutomaton(string name)
        {
            return automatons[name];
        }

        /// <summary>
        /// Adds the automaton to the society. It will be identified by name parameter.
        /// </summary>
        /// <param name="name">the name which this automaton identifies with</param>
        /// <param name="automaton">reference to the automaton</param>
        public void AddAutomaton(string name, BaseAutomaton automaton)
        {
            automatons.Add(name, automaton);
        }

        /// <summary>
        /// Removes the automaton with the name given as parameter.
        /// </summary>
        /// <param name="name">the name which this automaton identifies with</param>
        /// <returns>true if operation succeeded</returns>
        public bool RemoveAutomaton(String name)
        {
            return automatons.Remove(name);
        }

        /// <summary>
        /// Removes all automatons from the society.
        /// </summary>
        public void Clear()
        {
            automatons.Clear();
        }

        /// <summary>
        /// Starts all the automatons in the society.
        /// </summary>
        public void StartAutomatons()
        {
            foreach (BaseAutomaton automaton in automatons.Values)
            {
                automaton.Start();
            }
        }

        /// <summary>
        /// Stops all the automatons in the society.
        /// </summary>
        public void StopAutomatons()
        {
            foreach (BaseAutomaton automaton in automatons.Values)
            {
                automaton.StopSafely();
            }
        }

        /// <summary>
        /// Saves the state of the society.
        /// </summary>
        /// <returns>true if saving was successful or false otherwise</returns>
        public abstract bool SaveState();

        /// <summary>
        /// Loads the state of the society.
        /// </summary>
        /// <returns>true if loading was successful or false otherwise</returns>
        public abstract bool LoadState();

        /// <summary>
        /// Sends an XML automaton in the society with the specified name on the remote application.
        /// </summary>
        /// <param name="name">name with which the automaton is identified</param>
        /// <returns>false if automaton was not found or if it isn't an XMLAutomaton, true otherwise</returns>
        public bool SendXMLAutomatonToRemoteApplication(string name)
        {
            if (!automatons.ContainsKey(name))
            {
                return false;
            }
            BaseAutomaton automaton = automatons[name];
            if (automaton is XMLAutomaton)
            {
                ((XMLAutomaton)automaton).SaveSnapshot();
                automaton.StopSafely(() => { automataTransferAutomaton.AddMessage(new Message("", automaton)); });
                return true;
            }
            return false;
        }

        /// <summary>
        /// Sends a request to the remote application to serialize and sends back the automaton with the specified name.
        /// </summary>
        /// <param name="name">name with which the automaton is identified</param>
        public void RequestXMLAutomatonFromRemoteApplication(string name)
        {
            MessageRouter.Instance.SendRemote("AutomataTransferAutomaton", new Message("AutomataTransferAutomaton", name));
        }
    }
}
